<template>
  <ui-popover
    trigger="click"
    class="ui-tree-select"
    :class="[{'is-active': popoverVisible}, 'ui-tree-select--' + size]"
    :style="{'width': width}"
    placement="bottom-start"
    :popover-class="popoverClass"
    v-model="popoverVisible"
    @show="handlePopoverShow"
  >
    <div class="ui-tree-select__handle" slot="reference" ref="handle">
      <template v-if="isEmpty">
        <span class="ui-tree-select__placeholder">{{ placeholder }}</span>
      </template>
      <template v-else>
        <template v-if="multiple">
          <span class="ui-tree-select__selection">
            <ui-tag
              class="ui-tree-select__selection-item"
              v-for="(item, index) in currentItems"
              :key="item[valueField]"
              size="mini"
              type="info"
              :closable="true"
              @close="handleRemoveSelectClick(item, index)"
            >{{ item[textField] }}</ui-tag>
          </span>
        </template>
        <template v-else>
          <span class="ui-tree-select__value" :title="showText">{{ showText }}</span>
        </template>
        <span class="ui-tree-select__clear" v-if="clearable" title="删除" @click="handleClearClick"></span>
      </template>
      <span class="ui-tree-select__arrow el-icon-arrow-up"></span>
    </div>
    <div class="ui-tree-select__popover" :style="mainStyle">
      <slot name="popover-prepend"></slot>
      <div class="ui-tree-select__main" ref="main">
        <ui-tree
          ref="tree"
          v-model="innerValue"
          :default-expanded-level="defaultExpandedLevel"
          :multiple="multiple"
          :check-strictly="true"
          :node-key="valueField"
          :label-field="textField"
          :query-method="handleQueryData"
          :children-field="childrenField"
          @current-change="handleCurrentChange"
        ></ui-tree>
      </div>
    </div>
  </ui-popover>
</template>
<script>
import UiPopover from '../popover'
import UiTree from '../tree'
import UiTag from '../tag'
import Emitter from 'element-ui/src/mixins/emitter'

export default {
  name: 'UiTreeSelect',
  mixins: [Emitter],
  components: {
    UiPopover,
    UiTree,
    UiTag
  },
  props: {
    width: {
      type: String,
      default: '100%'
    },
    value: {
      type: [String, Array]
    },
    multiple: {
      type: Boolean
    },
    items: {
      type: Array,
      default: () => []
    },
    size: {
      type: String,
      default: 'small',
      validator(value) {
        return ['medium', 'small', 'mini'].includes(value)
      }
    },
    defaultExpandedLevel: {
      type: Number
    },
    placeholder: {
      type: String,
      default: '选择'
    },
    popoverClass: {
      type: String
    },
    valueField: {
      type: String,
      default: 'id'
    },
    textField: {
      type: String,
      default: 'label'
    },
    clearable: {
      type: Boolean,
      default: true
    },
    queryMethod: {
      type: Function
    },
    childrenField: {
      type: String,
      default: 'children'
    }
  },
  data() {
    return {
      mainWidth: null,
      innerItems: this.items || [],
      popoverVisible: false
    }
  },
  computed: {
    mainStyle() {
      const style = {}
      if (this.mainWidth) {
        style['min-width'] = this.mainWidth + 'px'
      }
      return style
    },
    isEmpty() {
      if (this.multiple) {
        return !this.value || !this.value.length
      }
      return !this.value
    },
    showText() {
      if (!this.value) {
        return
      }
      if (!this.currentItems || !this.currentItems.length) {
        return this.value
      }
      return this.currentItems.map(v => v[this.textField]).join('、')
    },
    innerItemsMap() {
      const map = {}
      const loop = (data) => {
        if (!data || !data.length) {
          return
        }
        data.forEach(v => {
          if (v[this.valueField]) {
            map[v[this.valueField]] = v
          }
          loop(v[this.childrenField])
        })
      }
      loop(this.innerItems)
      return map
    },
    currentItems() {
      if (this.isEmpty) {
        return []
      }
      const selection = this.multiple ? this.value : [this.value]
      return selection.reduce((rs, v) => {
        if (this.innerItemsMap[v]) {
          rs.push(this.innerItemsMap[v])
        }
        return rs
      }, [])
    },
    innerValue: {
      get() {
        return this.value
      },
      set(val) {
        this.$emit('input', val)
        this.dispatch('ElFormItem', 'el.form.change', val)
      }
    }
  },
  watch: {
    items() {
      this.innerItems = this.items || []
    }
  },
  methods: {
    handleQueryData() {
      if (!this.queryMethod) {
        return Promise.reject()
      }
      return this.queryMethod().then((rs) => {
        this.innerItems = rs || []
        return rs
      })
    },
    handlePopoverShow() {
      this.mainWidth = this.$refs.handle.clientWidth
      this.$nextTick(() => {
        this.scrollToCurrent()
      })
    },
    handleClearClick() {
      this.$emit('input', null)
    },
    handleCurrentChange() {
      this.$nextTick(() => {
        if (!this.multiple) {
          this.popoverVisible = false
        }
      })
    },
    handleRemoveSelectClick(item, index) {
      const nextCurrentItems = [...this.currentItems]
      nextCurrentItems.splice(index, 1)
      this.innerValue = nextCurrentItems.map(v => v[this.valueField])
    },
    filter(keyword) {
      return this.$refs.tree.filter(keyword)
    },
    scrollToCurrent() {
      if (!this.value || !this.$refs.main) {
        return
      }
      const $current = this.$refs.main.querySelector('.is-current')
      if (!$current) {
        return
      }
      this.$refs.main.scrollTop = $current.offsetTop
    }
  }
}
</script>
<style lang="less">
.ui-tree-select{
  display: inline-block;
  vertical-align: top;
  width: 100%;
}
.ui-tree-select__main{
  padding-top: 6px;
  padding-bottom: 6px;
  max-height: 300px;
  overflow: auto;
  .ui-tree {
    .el-tree-node.is-current{
      > .el-tree-node__content{
        color: #1aa2e8;
      }
    }
    .el-tree-node__content{
      padding-right: 12px;
    }
  }
}
.ui-tree-select__handle{
  display: block;
  padding-left: 12px;
  padding-right: 36px;
  border: 1px solid #ddd;
  border-radius: 3px;
  min-height: 24px;
  line-height: 24px;
  position: relative;
  transition: all .3s;
  cursor: pointer;
}
.ui-tree-select__arrow{
  height: 24px;
  width: 24px;
  text-align: center;
  line-height: 24px!important;
  position: absolute;
  right: 4px;
  top: 50%;
  margin-top: -12px;
  transform: rotate(180deg);
  opacity: .5;
  transition: transform .3s;
}
.ui-tree-select__placeholder{
  color: #aaa;
}
.ui-tree-select{
  &.is-active {
    .ui-tree-select__arrow {
      transform: rotate(0deg);
    }
    .ui-tree-select__handle {
      border-color: #1aa2e8;
      box-shadow: 0 0 0 0.2rem rgb(26 162 232 / 25%);
    }
  }
}
.ui-tree-select__clear{
  display: inline-block;
  width: 16px;
  height: 16px;
  background-color: #ddd;
  border-radius: 50%;
  position: relative;
  transform: rotate(45deg) translateY(-50%);
  margin-top: -3px;
  position: absolute;
  right: 12px;
  top: 50%;
  z-index: 3;
  cursor: pointer;
  transition: background-color .25s;
  &:before {
    content: '';
    width: 10px;
    height: 1px;
    background-color: #999;
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    transition: background-color .25s;
  }
  &:after {
    content: '';
    width: 1px;
    height: 10px;
    background-color: #999;
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    transition: background-color .25s;
  }
  &:hover {
    background-color: #999;
    &:after, &:before {
      background-color: #fff;
    }
  }
}
.ui-tree-select--small {
  .ui-tree-select__handle{
    padding-top: 3px;
    padding-bottom: 3px;
  }
}
.ui-tree-select--medium {
  .ui-tree-select__handle{
    padding-top: 5px;
    padding-bottom: 5px;
  }
}
.ui-tree-select--medium {
  .ui-tree-select__handle{
    padding-top: 5px;
    padding-bottom: 5px;
  }
}
.ui-tree-select--mini {
  .ui-tree-select__handle{
    padding-top: 1px;
    padding-bottom: 1px;
  }
}
.ui-tree-select__popover{
  padding: 6px 12px;
  box-sizing: border-box;
}
.ui-tree-select__selection-item{
  margin: 2px 6px 2px 0;
}
</style>
